package com.liush.system.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.liush.common.helper.JwtHelper;
import com.liush.common.result.Result;
import com.liush.common.result.ResultCodeEnum;
import com.liush.common.util.ResponseUtil;
import com.liush.model.controller.SysLoginLog;
import com.liush.model.vo.LoginVo;
import com.liush.system.custom.CustomUser;
import com.liush.system.service.LoginLogService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: liush
 * @CreateTime: 2023-03-05  11:02
 * @Description: 登录过滤器，继承UsernamePasswordAuthenticationFilter，对用户名密码进行登录校验
 */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;
    private LoginLogService loginLogService;

    public TokenLoginFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate, LoginLogService loginLogService) {
        this.setAuthenticationManager(authenticationManager);
        //设置过滤器要过滤器的请求地址
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login", "POST"));
        this.redisTemplate = redisTemplate;
        this.loginLogService = loginLogService;
    }

    /**
     * 登录认证
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        try {
            LoginVo loginVo = new ObjectMapper().readValue(request.getInputStream(), LoginVo.class);
            String username = loginVo.getUsername();
            String password = loginVo.getPassword();
            if (username == null) {
                username = "";
            }
            if (password == null) {
                password = "";
            }
            username = username.trim();
            //authRequest 未认证的用户对象
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                    username, password);
            return this.getAuthenticationManager().authenticate(authRequest);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 登录成功
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        // 获取认证对象
        CustomUser customUser = (CustomUser) authResult.getPrincipal();
        // 生成token
        Long userId = customUser.getSysUser().getId();
        String userName = customUser.getSysUser().getUsername();
        // 把用户的权限保存到redis
        Collection<GrantedAuthority> authorities = customUser.getAuthorities();
        redisTemplate.boundValueOps(userName).set(authorities);
        //保存当前登录用户的日志信息
        SysLoginLog sysLoginLog = new SysLoginLog();
        sysLoginLog.setUsername(userName);
        sysLoginLog.setStatus(1);
        sysLoginLog.setMsg("登陆成功");
        sysLoginLog.setIpaddr(request.getLocalAddr());
        sysLoginLog.setAccessTime(new Date());
        loginLogService.saveLoginLog(sysLoginLog);
        // 认证成功向客户端写入token
        String token = JwtHelper.createToken(userId, userName);
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        ResponseUtil.out(response, Result.ok(map));
    }

    /**
     * 登录失败
     *
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        if (failed.getCause() instanceof RuntimeException) {
            ResponseUtil.out(response, Result.fail("登录超时"));
        } else {
            ResponseUtil.out(response, Result.fail(failed.getMessage()));
        }
    }
}
